import components from 'components/componentDefines';
import { VirtualMobile, VirtualMobileContext } from 'components/virtualMobile';
import { MobilePage, MobilePageControl } from 'components/mobilePage';
import { Editor, EditorProps } from 'components/editor';
import { Control } from 'components/common';
import { StationService } from 'admin/services/station';
import { siteMap } from 'admin/pageNodes';
import { siteMap as userSiteMap } from 'user/site';
import { StationService as UserStationService } from 'user/services/stationService';
import { UserPage as UserPage, UserApplication } from 'user/application';
import { FormValidator, rules } from 'maishu-dilu';

import * as ui from "maishu-ui-toolkit";
import 'jquery-ui';
import { showTemplateDialog } from 'admin/controls/templateDialog';
import { MemberService } from 'user/services/memberService';
import React = require('react');
import ReactDOM = require('react-dom');
import * as chitu from "maishu-chitu";
import { AdminApplication } from 'admin/application';

export interface Props extends React.Props<MobilePageDesigner> {
    pageData: PageData,
    showComponentToolbar: boolean,
    showPageEditor?: boolean,
    // showMenuSwitch?: boolean,
    save: (pageData: PageData) => Promise<PageData>,
    leftButtons?: JSX.Element[]
    rightButtons?: JSX.Element[],
    showTemplateButton?: boolean,
    showSnapshootButton?: boolean,
    elementPage: chitu.Page,
    panel?: JSX.Element,
}

export interface State {
    editors: React.ReactElement<any>[],
    pageData: PageData,
    snapshootCount?: number,
    // selectedControl?: MobilePageControl,
    styleColor: StyleColor,
    template?: PageData,
}

export class MobilePageDesigner extends React.Component<Props, State> {
    private validator: FormValidator;
    private nameInput: HTMLInputElement;
    private virtualMobile: VirtualMobile;
    private currentEditor: HTMLElement;
    private element: HTMLElement;
    private editorName: string;
    private form: HTMLElement;
    private editors = new Array<Editor<EditorProps, any>>();
    private originalPageData: PageData;

    //========================================

    mobilePage: MobilePage;
    saved: chitu.Callback1<MobilePageDesigner, { pageData: PageData }>;
    componentEditor: ComponentEditor;
    remarkInput: HTMLInputElement;

    constructor(props: Props) {
        super(props);
        if (this.props.pageData == null)
            throw new Error("Property of pageData cannt be null.");


        let pageData = this.props.pageData;
        //======================================================
        // 保留一份副本，用于比较判断 pagedata 是否更改
        this.originalPageData = JSON.parse(JSON.stringify(pageData));
        this.state = {
            editors: [], pageData: props.pageData,
            styleColor: "default"
        };

        this.saved = chitu.Callbacks<MobilePageDesigner, { pageData: PageData }>();
        StationService.recorvered.add((sender, pageData) => {
            Object.assign(this.props.pageData, pageData);
            this.setState(this.state);
        })
    }

    async setMenu(value) {
        let { pageData } = this.state;
        if (value) {
            let station = this.props.elementPage.createService(UserStationService);
            let menuPageData = await station.pages.menu();
            let menuControlData = menuPageData.controls.filter(o => o.controlName == 'menu')[0];
            menuControlData.selected = "disabled";
            console.assert(menuControlData != null);
            pageData.controls.push(menuControlData);
        }
        else {
            var controls = pageData.controls.filter(o => o.controlName != 'menu');
            pageData.controls = controls;
        }
        this.setState({ pageData });
    }

    private hasMenu() {
        let { pageData } = this.state
        var controls = pageData.controls.filter(o => o.controlName == 'menu');
        return controls.length > 0
    }

    async save() {
        if (this.validator != null) {
            this.validator.clearErrors();
            let isValid = await this.validator.check();
            if (!isValid) {
                return Promise.reject('validate fail');
            }
        }

        for (let i = 0; i < this.editors.length; i++) {
            if (this.editors[i].validate) {
                let result = await this.editors[i].validate();
                if (!result) {
                    this.componentEditor.setSelectedControl(this.editors[i].props.control as MobilePageControl)
                    return Promise.reject(`Editor validate fail`);
                }
            }
        }

        let { pageData } = this.props;
        this.pushControlValues(pageData.controls);

        if (this.nameInput)
            pageData.name = this.nameInput.value;

        if (this.remarkInput)
            pageData.remark = this.remarkInput.value;

        let save = this.props.save;
        return save(pageData).then(data => {
            this.saved.fire(this, { pageData })
            this.originalPageData = JSON.parse(JSON.stringify(pageData));
            return data;
        });
    }

    /**
     * 件页面上控件的值，写进 pageData
     */
    private pushControlValues(controls: ControlDescrtion[]) {
        for (let i = 0; i < controls.length; i++) {
            let componet = (this.mobilePage.controls.filter(c => c.controlId == controls[i].controlId)[0]) as any as Control<any, any>;
            console.assert(componet != null);

            let keys = componet.persistentMembers || [];
            let data = {};
            for (let i = 0; i < keys.length; i++) {
                let key = keys[i];
                data[key] = componet.state[key];
            }
            controls[i].data = data;
        }
    }

    get hasChanged() {
        this.pushControlValues(this.props.pageData.controls);
        let isChanged = !this.isEquals(this.originalPageData, this.props.pageData);
        return isChanged;
    }

    async saveSnapshoot() {
        let { pageData } = this.props;
        console.assert(pageData != null);

        let hasChanged = this.hasChanged;
        if (hasChanged || !pageData.id) {
            ui.alert({ title: '提示', message: `保存快照前必须先保存页面, 请点击"保存"按钮保存页面` });
            return Promise.reject("page data is not save.");
        }

        let station = (this.props.elementPage.app as AdminApplication).createService(StationService);
        return station.saveSnapshoot(pageData);
    }

    removeControl(controlId: string) {
        let { pageData } = this.props;

        pageData.controls = pageData.controls.filter(o => o.controlId != controlId);

        this.setState(this.state);
        return Promise.resolve();
    }

    preview() {
        let { pageData } = this.props;
        console.assert(pageData != null);

        let hasChanged = this.hasChanged;
        if (hasChanged || !pageData.id) {
            ui.alert({ title: '提示', message: `预览前必须先保存页面, 请点击"保存"按钮保存页面` });
            return;
        }
        let url = UserApplication.createUrl(userSiteMap.nodes.page, { pageId: pageData.id });
        open(url, '_blank');
    }

    changePageTemplate() {
        showTemplateDialog((pageData) => {
            let obj: PageData = JSON.parse(JSON.stringify(pageData));
            //=====================================
            // 清除控件编辑器
            // this.editorsElement.innerHTML = "";
            //=====================================

            this.props.pageData.controls = obj.controls;
            this.setState(this.state);
        });
    }

    private isEquals(obj1: object, obj2: object) {
        var type = typeof obj1;
        if (type == 'number' || type == 'string' || obj1 instanceof Date) {
            return obj1 == obj2;
        }

        for (var key in obj1) {
            let value1 = obj1[key];
            let value2 = obj2[key];

            if (!this.isEquals(value1, value2)) {
                return false;
            }
        }

        return true;
    }

    componentWillReceiveProps(props: Props) {
        this.setState({ pageData: props.pageData })
    }

    async componentDidMount() {
        if (this.props.showPageEditor) {
            this.validator = new FormValidator(this.form,
                { name: 'name', rules: [rules.required('请输入页面名称')] }
            )
        }

        if (this.props.pageData.id) {
            let station = (this.props.elementPage.app as AdminApplication).createService(StationService);
            let snapshootCount = await station.snapshootCounter(this.props.pageData.id);
            this.setState({ snapshootCount: snapshootCount.value })
            snapshootCount.add((value) => {
                this.setState({ snapshootCount: value })
            })
        }

        let pageData = this.props.pageData
        let member = await this.props.elementPage.createService(MemberService);
        let store = await member.store();
        this.setState({ styleColor: store.Style })

        let template: PageData;
        if (pageData.templateId) {
            let station = this.props.elementPage.createService(UserStationService);
            template = !pageData.templateId ? null : await station.pages.pageDataById(pageData.templateId);
            this.setState({ template })
        }

        //=======================================================================
        // 让工具栏的图标可以拖动到移动端页面
        setTimeout(() => {
            $(this.componentEditor.allContainer).find('li').draggable({
                connectToSortable: $(this.mobilePage.props.elementPage.element).find("section"),
                helper: "clone",
                revert: "invalid"
            });
        }, 1000);
        //=======================================================================

    }

    onMobilePageCreated(mobilePage: MobilePage) {
        if (mobilePage.selecteControl) {
            this.componentEditor.setSelectedControl(mobilePage.selecteControl.control);
        }
    }

    render() {
        let h = React.createElement;
        let { snapshootCount, pageData, styleColor, template } = this.state;
        let { showComponentToolbar: showComponentToolbar, rightButtons, leftButtons,
            showTemplateButton, showSnapshootButton, } = this.props;
        return (
            <div ref={(e: HTMLElement) => this.element = e || this.element}>
                <div style={{ position: 'absolute' }}>
                    <VirtualMobile ref={(e) => this.virtualMobile = e || this.virtualMobile} >
                        <VirtualMobileContext.Consumer>
                            {args => {
                                console.assert(args.page != null)
                                return <MobilePage pageData={pageData}
                                    template={template}
                                    styleColor={styleColor}
                                    elementPage={args.page}
                                    ref={(e) => {
                                        if (e != null && this.mobilePage == null) {
                                            this.onMobilePageCreated(e)
                                        }
                                        this.mobilePage = e || this.mobilePage
                                    }}
                                    controlCreated={(c, isSelected) => {
                                        if (isSelected) {
                                            this.componentEditor.setSelectedControl(c)
                                        }
                                        c.stateChanged.add((sender, value) => {
                                            debugger
                                            let d = pageData.controls.filter(o => o.controlId == sender.id)[0];
                                            console.assert(d != null);
                                            this.pushControlValues([d]);
                                        })
                                    }}
                                    designTime={{
                                        controlSelected: (a, b) => {
                                            let controlData = pageData.controls.filter(o => o.controlId == a.id)[0];
                                            console.assert(controlData != null);
                                            pageData.controls.forEach(o => o.selected = false);
                                            controlData.selected = true;
                                            this.componentEditor.setSelectedControl(a)
                                        }
                                    }} />
                            }}
                        </VirtualMobileContext.Consumer>
                    </VirtualMobile>
                </div>

                <div style={{ paddingLeft: 390 }} >
                    <ul style={{ margin: 0 }}>
                        <li className="pull-right">
                            {leftButtons}
                            {showTemplateButton ?
                                <button className="btn btn-sm btn-primary"
                                    onClick={() => this.changePageTemplate()}>
                                    <i className="icon-copy" />
                                    <span>更换模板</span>
                                </button> : null}
                            {showSnapshootButton ?
                                <div className="btn-group">
                                    <button className="btn btn-sm btn-primary dropdown-toggle"
                                        ref={(e: HTMLButtonElement) => {
                                            if (!e) return;
                                            ui.buttonOnClick(e, () => {
                                                return this.saveSnapshoot();
                                            }, { toast: '保存快照成功' })
                                        }}>
                                        <i className="icon-camera" />
                                        <span>存为快照</span>
                                    </button>
                                    <button type="button" className="btn btn-sm btn-primary dropdown-toggle"
                                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                        <i className="icon-caret-down"></i>
                                    </button>
                                    <ul className="dropdown-menu">
                                        <li style={{ padding: '6px 0' }}>
                                            <a className="btn-link"
                                                onClick={() => (this.props.elementPage.app as AdminApplication).redirect(siteMap.nodes.station_snapshoots, { pageDataId: pageData.id })}>
                                                查看快照
                                                <span className="badge pull-right">
                                                    {snapshootCount}
                                                </span>
                                            </a>
                                        </li>
                                    </ul>
                                </div> : null}
                            <button className="btn btn-sm btn-primary" onClick={() => this.preview()}>
                                <i className="icon-eye-open" />
                                <span>预览</span>
                            </button>
                            <button className="btn btn-sm btn-primary"
                                ref={(e: HTMLButtonElement) => e != null ? ui.buttonOnClick(e, () => this.save(), { toast: '保存页面成功' }) : null}>
                                <i className="icon-save" />
                                <span>保存</span>
                            </button>
                            {rightButtons}
                        </li>
                        <li className="clearfix">
                        </li>
                    </ul>

                    <div className="clear-fix" />
                    <hr style={{ margin: 0 }} />

                    {this.props.showPageEditor ? <React.Fragment>
                        <div className="form-group"
                            ref={(e: HTMLElement) => this.form = e || this.form}
                            style={{ height: 40, display: this.props.showPageEditor == true ? 'block' : 'none', marginTop: 20 }}>
                            <div className="row">
                                <div className="col-sm-4">
                                    <label className="control-label pull-left" style={{ paddingTop: 8 }}>名称</label>
                                    <div style={{ paddingLeft: 40 }}>
                                        <input name="name" className="form-control" placeholder="请输入页面名称（必填）"
                                            ref={(e: HTMLInputElement) => {
                                                this.nameInput = e || this.nameInput;
                                                this.nameInput.value = pageData.name || '';
                                            }} />
                                    </div>
                                </div>
                                <div className="col-sm-8">
                                    <label className="control-label pull-left" style={{ paddingTop: 8 }}>备注</label>
                                    <div style={{ paddingLeft: 40 }}>
                                        <input name="remark" className="form-control pull-left" placeholder="请输入页面备注（选填）"
                                            ref={(e: HTMLInputElement) => {
                                                if (!e) return;
                                                this.remarkInput = e || this.remarkInput;
                                                e.value = pageData.remark || '';
                                                e.onchange = () => {
                                                    pageData.remark = e.value;
                                                }
                                            }} />
                                    </div>
                                </div>
                            </div>
                        </div>
                        <hr />
                    </React.Fragment> : null}
                    <div className="pull-left" style={{ width: `calc(100% - 260px)` }}>
                        <ComponentEditor showComponentToolbar={showComponentToolbar}
                            panel={this.props.panel}
                            removeControl={(controlId) => this.removeControl(controlId)}
                            ref={e => {
                                if (!e) return
                                this.componentEditor = e || this.componentEditor
                            }} />
                    </div>
                    <div className="pull-right" style={{ width: 240, marginTop: showComponentToolbar ? 35 : 15 }}>
                        <div className="panel panel-default">
                            <div className="panel-heading">页面设置{showComponentToolbar}</div>
                            <ul className="list-group">
                                <li className="list-group-item">
                                    <div className="pull-left" style={{ width: "calc(100% - 60px)" }}>
                                        显示导航菜单
                                        </div>
                                    <label className="switch">
                                        <input type="checkbox" className="ace ace-switch ace-switch-5"
                                            ref={(e: HTMLInputElement) => {
                                                if (!e) return;

                                                e.checked = this.hasMenu();
                                                e.onchange = async () => {
                                                    this.setMenu(e.checked)
                                                }
                                            }} />
                                        <span className="lbl middle"></span>
                                    </label>
                                </li>
                                <li className="list-group-item">
                                    <div className="pull-left" style={{ width: "calc(100% - 60px)" }}>
                                        显示购物车结算栏
                                        </div>
                                    <label className="switch">
                                        <input type="checkbox" className="ace ace-switch ace-switch-5" />
                                        <span className="lbl middle"></span>
                                    </label>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}


class ComponentEditor extends React.Component<{
    showComponentToolbar: boolean,
    //  selectedControl?: MobilePageControl,
    panel?: JSX.Element,
    removeControl: ((controlId: string) => Promise<any>)
}> {
    editorsElement: HTMLElement;
    private editorNameElement: HTMLElement;
    allContainer: HTMLElement;
    editorName: string;
    private currentEditor: HTMLElement;
    private editors: Editor<any, any>[] = [];
    private selectedControl: MobilePageControl;

    constructor(props) {
        super(props)

    }

    setSelectedControl(selectedControl: MobilePageControl) {
        this.selectedControl = selectedControl;
        this.loadControlEditor(selectedControl);
    }

    private loadControlEditor(control: MobilePageControl) {
        if (!control)
            return;

        console.assert(control.id != null && control.id != '');
        if (!control.hasEditor) {
            return;
        }

        let controlName = control.controlName;
        let editorPathName = Editor.path(controlName);
        let editorId = `editor-${control.id}`;
        let editorElement = this.editorsElement.querySelector(`[id='${editorId}']`) as HTMLElement;

        if (editorElement == null) {
            editorElement = document.createElement('div');
            editorElement.className = `${controlName}-editor`;
            editorElement.id = editorId;
            this.editorsElement.appendChild(editorElement);

            requirejs([editorPathName], (exports) => {
                let editorType = exports.default;
                console.assert(editorType != null, 'editor type is null');
                console.assert(control.elementPage != null, 'element page is null');
                let editorReactElement = React.createElement(editorType, { control, elementPage: control.elementPage } as EditorProps);
                let editor: Editor<any, any> = ReactDOM.render(editorReactElement, editorElement) as any;
                this.editors.push(editor);
            })
        }

        this.editorName = components.filter(o => o.name == controlName).map(o => o.displayName)[0] || controlName;
        if (this.editorNameElement)
            this.editorNameElement.innerHTML = this.editorName;

        if (this.currentEditor) {
            this.currentEditor.style.display = "none";
        }

        editorElement.style.removeProperty("display");
        this.currentEditor = editorElement;

    }



    render() {
        let { showComponentToolbar, removeControl } = this.props;
        return <React.Fragment>
            <div className="form-group">
                <h5 style={{ display: showComponentToolbar == true ? 'block' : 'none' }}>页面组件</h5>
                <ul ref={(e: HTMLElement) => this.allContainer = e || this.allContainer} className="clearfix"
                    style={{
                        padding: 0, listStyle: 'none',
                        display: showComponentToolbar == true ? 'block' : 'none'
                    }}>
                    {components.filter(o => o.visible != false).map((c, i) =>
                        <li key={c.name} data-control-name={c.name} data-target={c.target}
                            style={{
                                float: 'left', height: 80, width: 80, border: 'solid 1px #ccc', marginLeft: 4,
                                textAlign: 'center', paddingTop: 8, backgroundColor: 'white', zIndex: 100
                            }} >
                            <div className="btn-link">
                                <i className={c.icon} style={{ fontSize: 44, color: 'black' }} />
                            </div>
                            <div>
                                {c.displayName}
                            </div>
                        </li>
                    )}
                </ul>
            </div>
            {this.props.panel ? <div className="form-group">
                {this.props.panel}
            </div> : null}
            {showComponentToolbar ?
                <div className="form-group">
                    <div className="well">
                        <i className="icon-remove" style={{ cursor: 'pointer' }}
                            ref={(e: HTMLElement) => {
                                if (e == null) return;
                                e.onclick = ui.buttonOnClick(
                                    () => removeControl(this.selectedControl.id),
                                    {
                                        confirm: () => {
                                            return `确定要移除控件'${this.editorNameElement.innerHTML}'吗？`
                                        }
                                    });

                            }}></i>
                        <span style={{ paddingLeft: 8 }}
                            ref={(e: HTMLElement) => this.editorNameElement = e || this.editorNameElement} />
                        <hr style={{ marginTop: 14 }} />
                        <div ref={(e: HTMLElement) => this.editorsElement = e || this.editorsElement}>
                        </div>
                    </div>
                </div> :
                <div ref={(e: HTMLElement) => this.editorsElement = e || this.editorsElement}>
                </div>
            }
        </React.Fragment>
    }


}
